home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 126-150 / disk_129 / patch / ver1.3 / patch.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  43KB  |  1,903 lines

  1. /* patch - a program to apply diffs to original files
  2.  *
  3.  * $Header: patch.c,v 1.3 85/03/26 15:07:43 lwall Exp $
  4.  *
  5.  * Copyright 1984, Larry Wall
  6.  *
  7.  * This program may be copied as long as you don't try to make any
  8.  * money off of it, or pretend that you wrote it.
  9.  *
  10.  * $Log:    patch.c,v $
  11.  *
  12.  * Revision 1.3.2R  88/01/14  R. Coupland
  13.  * Created amiga version using Lattice C version 4.0.
  14.  * All changes for the amiga were made under "#ifdef AMIGA" conditional.
  15.  * Amiga version supports all features except "ed scripts".
  16.  * 
  17.  * Revision 1.3.1R 87/07/31 R. Coupland
  18.  * Added -m option which requires an exact match between patern file and
  19.  * input file else abort.
  20.  *
  21.  * Revision 1.3  85/03/26  15:07:43  lwall
  22.  * Frozen.
  23.  * 
  24.  * Revision 1.2.1.9  85/03/12  17:03:35  lwall
  25.  * Changed pfp->_file to fileno(pfp).
  26.  * 
  27.  * Revision 1.2.1.8  85/03/12  16:30:43  lwall
  28.  * Check i_ptr and i_womp to make sure they aren't null before freeing.
  29.  * Also allow ed output to be suppressed.
  30.  * 
  31.  * Revision 1.2.1.7  85/03/12  15:56:13  lwall
  32.  * Added -p option from jromine@uci-750a.
  33.  * 
  34.  * Revision 1.2.1.6  85/03/12  12:12:51  lwall
  35.  * Now checks for normalness of file to patch.
  36.  * 
  37.  * Revision 1.2.1.5  85/03/12  11:52:12  lwall
  38.  * Added -D (#ifdef) option from joe@fluke.
  39.  * 
  40.  * Revision 1.2.1.4  84/12/06  11:14:15  lwall
  41.  * Made smarter about SCCS subdirectories.
  42.  * 
  43.  * Revision 1.2.1.3  84/12/05  11:18:43  lwall
  44.  * Added -l switch to do loose string comparison.
  45.  * 
  46.  * Revision 1.2.1.2  84/12/04  09:47:13  lwall
  47.  * Failed hunk count not reset on multiple patch file.
  48.  * 
  49.  * Revision 1.2.1.1  84/12/04  09:42:37  lwall
  50.  * Branch for sdcrdcf changes.
  51.  * 
  52.  * Revision 1.2  84/11/29  13:29:51  lwall
  53.  * Linted.  Identifiers uniqified.  Fixed i_ptr malloc() bug.  Fixed
  54.  * multiple calls to mktemp().  Will now work on machines that can only
  55.  * read 32767 chars.  Added -R option for diffs with new and old swapped.
  56.  * Various cosmetic changes.
  57.  * 
  58.  * Revision 1.1  84/11/09  17:03:58  lwall
  59.  * Initial revision
  60.  * 
  61.  */
  62.  
  63. #define DEBUGGING
  64.  
  65. /* shut lint up about the following when return value ignored */
  66.  
  67. #define Signal (void)signal
  68. #define Unlink (void)unlink
  69. #define Lseek (void)lseek
  70. #define Fseek (void)fseek
  71. #define Fstat (void)fstat
  72. #define Pclose (void)pclose
  73. #define Close (void)close
  74. #define Fclose (void)fclose
  75. #define Fflush (void)fflush
  76. #define Sprintf (void)sprintf
  77. #define Mktemp (void)mktemp
  78. #define Strcpy (void)strcpy
  79. #define Strcat (void)strcat
  80.  
  81. #include <stdio.h>
  82. #include <assert.h>
  83. #ifndef AMIGA
  84. #include <sys/types.h>
  85. #include <sys/stat.h>
  86. #endif AMIGA
  87. #include <ctype.h>
  88. #include <signal.h>
  89.  
  90. /* constants */
  91.  
  92. #define TRUE (1)
  93. #define FALSE (0)
  94.  
  95. #define MAXHUNKSIZE 500
  96. #define MAXLINELEN 1024
  97. #define BUFFERSIZE 1024
  98. #define ORIGEXT ".orig"
  99. #define SCCSPREFIX "s."
  100. #define GET "get -e %s"
  101. #define RCSSUFFIX ",v"
  102. #define CHECKOUT "co -l %s"
  103.  
  104. /* handy definitions */
  105.  
  106. #define Null(t) ((t)0)
  107. #define Nullch Null(char *)
  108. #define Nullfp Null(FILE *)
  109.  
  110. #define Ctl(ch) (ch & 037)
  111.  
  112. #define strNE(s1,s2) (strcmp(s1,s2))
  113. #define strEQ(s1,s2) (!strcmp(s1,s2))
  114. #define strnNE(s1,s2,l) (strncmp(s1,s2,l))
  115. #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
  116.  
  117. /* typedefs */
  118.  
  119. typedef char bool;
  120. typedef long LINENUM;            /* must be signed */
  121. typedef unsigned MEM;            /* what to feed malloc */
  122.  
  123. /* globals */
  124.  
  125. int Argc;                /* guess */
  126. char **Argv;
  127.  
  128. #ifndef AMIGA
  129. struct stat filestat;            /* file statistics area */
  130.  
  131. #endif AMIGA
  132. char serrbuf[BUFSIZ];            /* buffer for stderr */
  133. char buf[MAXLINELEN];            /* general purpose buffer */
  134. FILE *pfp = Nullfp;            /* patch file pointer */
  135. FILE *ofp = Nullfp;            /* output file pointer */
  136. FILE *rejfp = Nullfp;            /* reject file pointer */
  137.  
  138. LINENUM input_lines = 0;        /* how long is input file in lines */
  139. LINENUM last_frozen_line = 0;        /* how many input lines have been */
  140.                     /* irretractibly output */
  141.  
  142. #define MAXFILEC 2
  143. int filec = 0;                /* how many file arguments? */
  144. char *filearg[MAXFILEC];
  145.  
  146. char *outname = Nullch;
  147. char rejname[128];
  148.  
  149. char *origext = Nullch;
  150.  
  151. #ifndef AMIGA
  152. char TMPOUTNAME[] = "/tmp/patchoXXXXXX";
  153. char TMPINNAME[] = "/tmp/patchiXXXXXX";    /* you might want /usr/tmp here */
  154. char TMPREJNAME[] = "/tmp/patchrXXXXXX";
  155. char TMPPATNAME[] = "/tmp/patchpXXXXXX";
  156. #else
  157. char TMPOUTNAME[] = "ram:patchoXXXXXX";
  158. char TMPINNAME[] = "ram:patchiXXXXXX";    /* you might want /usr/tmp here */
  159. char TMPREJNAME[] = "ram:patchrXXXXXX";
  160. char TMPPATNAME[] = "ram:patchpXXXXXX";
  161. #endif AMIGA
  162.  
  163. LINENUM last_offset = 0;
  164. #ifdef DEBUGGING
  165. int debug = 0;
  166. #endif
  167. bool verbose = TRUE;
  168. bool reverse = FALSE;
  169. bool usepath = FALSE;
  170. bool canonicalize = FALSE;
  171. bool ematch = FALSE;
  172.  
  173. #define CONTEXT_DIFF 1
  174. #define NORMAL_DIFF 2
  175. #define ED_DIFF 3
  176. int diff_type = 0;
  177.  
  178. int do_defines = 0;            /* patch using ifdef, ifndef, etc. */
  179. char if_defined[128];            /* #ifdef xyzzy */
  180. char not_defined[128];            /* #ifndef xyzzy */
  181. char else_defined[] = "#else\n";    /* #else */
  182. char end_defined[128];            /* #endif xyzzy */
  183.  
  184. char *revision = Nullch;        /* prerequisite revision, if any */
  185.  
  186. /* procedures */
  187.  
  188. LINENUM locate_hunk();
  189. bool patch_match();
  190. bool similar();
  191. char *malloc();
  192. char *savestr();
  193. char *strcpy();
  194. char *strcat();
  195. #ifndef AMIGA
  196. char *sprintf();        /* usually */
  197. #endif AMIGA
  198. int my_exit();
  199. bool rev_in_string();
  200. char *fetchname();
  201. long atol();
  202. long lseek();
  203. char *mktemp();
  204.  
  205. /* patch type */
  206.  
  207. bool there_is_another_patch();
  208. bool another_hunk();
  209. char *pfetch();
  210. int pch_line_len();
  211. LINENUM pch_first();
  212. LINENUM pch_ptrn_lines();
  213. LINENUM pch_newfirst();
  214. LINENUM pch_repl_lines();
  215. LINENUM pch_end();
  216. LINENUM pch_context();
  217. LINENUM pch_hunk_beg();
  218. char pch_char();
  219. char *pfetch();
  220. char *pgets();
  221.  
  222. /* input file type */
  223.  
  224. char *ifetch();
  225.  
  226. /* apply a context patch to a named file */
  227.  
  228. main(argc,argv)
  229. int argc;
  230. char **argv;
  231. {
  232.     LINENUM where;
  233.     int hunk = 0;
  234.     int failed = 0;
  235.     int i;
  236.  
  237.     setbuf(stderr,serrbuf);
  238.     for (i = 0; i<MAXFILEC; i++)
  239.     filearg[i] = Nullch;
  240.     Mktemp(TMPOUTNAME);
  241.     Mktemp(TMPINNAME);
  242.     Mktemp(TMPREJNAME);
  243.     Mktemp(TMPPATNAME);
  244.  
  245.     /* parse switches */
  246.     Argc = argc;
  247.     Argv = argv;
  248.     get_some_switches();
  249.     
  250.     /* make sure we clean up /tmp in case of disaster */
  251.     set_signals();
  252.  
  253.     for (
  254.     open_patch_file(filearg[1]);
  255.     there_is_another_patch();
  256.     reinitialize_almost_everything()
  257.     ) {                    /* for each patch in patch file */
  258.  
  259.     if (outname == Nullch)
  260.         outname = savestr(filearg[0]);
  261.     
  262.     /* initialize the patched file */
  263.     init_output(TMPOUTNAME);
  264.     
  265.     /* for ed script just up and do it and exit */
  266.     if (diff_type == ED_DIFF) {
  267.         do_ed_script();
  268.         continue;
  269.     }
  270.     
  271.     /* initialize reject file */
  272.     init_reject(TMPREJNAME);
  273.     
  274.     /* find out where all the lines are */
  275.     scan_input(filearg[0]);
  276.     
  277.     /* from here on, open no standard i/o files, because malloc */
  278.     /* might misfire */
  279.     
  280.     /* apply each hunk of patch */
  281.     hunk = 0;
  282.     failed = 0;
  283.     while (another_hunk()) {
  284.         hunk++;
  285.         where = locate_hunk();
  286.         if (hunk == 1 && where == Null(LINENUM) && !ematch) {
  287.                     /* dwim for reversed patch? */
  288.         pch_swap();
  289.         reverse = !reverse;
  290.         where = locate_hunk();    /* try again */
  291.         if (where == Null(LINENUM)) {
  292.             pch_swap();        /* no, put it back to normal */
  293.             reverse = !reverse;
  294.         }
  295.         else {
  296.             say("%seversed (or previously applied) patch detected!  %s -R.\n",
  297.             reverse ? "R" : "Unr",
  298.             reverse ? "Assuming" : "Ignoring");
  299.         }
  300.         }
  301.         if (where == Null(LINENUM)) {
  302.         if (ematch)
  303.             fatal("Patch file didn't match input file");
  304.         abort_hunk();
  305.         failed++;
  306.         if (verbose)
  307.             say("Hunk #%d failed.\n",hunk);
  308.         }
  309.         else {
  310.         apply_hunk(where);
  311.         if (verbose)
  312.             if (last_offset)
  313.             say("Hunk #%d succeeded (offset %d line%s).\n",
  314.               hunk,last_offset,last_offset==1?"":"s");
  315.             else
  316.             say("Hunk #%d succeeded.\n", hunk);
  317.         }
  318.     }
  319.     
  320.     assert(hunk);
  321.     
  322.     /* finish spewing out the new file */
  323.     spew_output();
  324.     
  325.     /* and put the output where desired */
  326.     ignore_signals();
  327.     move_file(TMPOUTNAME,outname);
  328.     Fclose(rejfp);
  329.     rejfp = Nullfp;
  330.     if (failed) {
  331.         if (!*rejname) {
  332.         Strcpy(rejname, outname);
  333.         Strcat(rejname, ".rej");
  334.         }
  335.         say("%d out of %d hunks failed--saving rejects to %s\n",
  336.         failed, hunk, rejname);
  337.         move_file(TMPREJNAME,rejname);
  338.     }
  339.     set_signals();
  340.     }
  341.     my_exit(0);
  342. }
  343.  
  344. reinitialize_almost_everything()
  345. {
  346.     re_patch();
  347.     re_input();
  348.  
  349.     input_lines = 0;
  350.     last_frozen_line = 0;
  351.  
  352.     filec = 0;
  353.     if (filearg[0] != Nullch) {
  354.     free(filearg[0]);
  355.     filearg[0] = Nullch;
  356.     }
  357.  
  358.     if (outname != Nullch) {
  359.     free(outname);
  360.     outname = Nullch;
  361.     }
  362.  
  363.     last_offset = 0;
  364.  
  365.     diff_type = 0;
  366.  
  367.     if (revision != Nullch) {
  368.     free(revision);
  369.     revision = Nullch;
  370.     }
  371.  
  372.     reverse = FALSE;
  373.  
  374.     get_some_switches();
  375.  
  376.     if (filec >= 2)
  377.     fatal("You may not change to a different patch file.\n");
  378. }
  379.  
  380. get_some_switches()
  381. {
  382.     register char *s;
  383.  
  384.     rejname[0] = '\0';
  385.     if (!Argc)
  386.     return;
  387.     for (Argc--,Argv++; Argc; Argc--,Argv++) {
  388.     s = Argv[0];
  389.     if (strEQ(s,"+")) {
  390.         return;            /* + will be skipped by for loop */
  391.     }
  392.     if (*s != '-' || !s[1]) {
  393.         if (filec == MAXFILEC)
  394.         fatal("Too many file arguments.\n");
  395.         filearg[filec++] = savestr(s);
  396.     }
  397.     else {
  398.         switch (*++s) {
  399.         case 'b':
  400.         origext = savestr(Argv[1]);
  401.         Argc--,Argv++;
  402.         break;
  403.         case 'c':
  404.         diff_type = CONTEXT_DIFF;
  405.         break;
  406.         case 'd':
  407.         if (chdir(Argv[1]) < 0)
  408.             fatal("Can't cd to %s.\n",Argv[1]);
  409.         Argc--,Argv++;
  410.         break;
  411.         case 'D':
  412.             do_defines++;
  413.         Sprintf(if_defined, "#ifdef %s\n", Argv[1]);
  414.         Sprintf(not_defined, "#ifndef %s\n", Argv[1]);
  415.         Sprintf(end_defined, "#endif %s\n", Argv[1]);
  416.         Argc--,Argv++;
  417.         break;
  418.         case 'e':
  419.         diff_type = ED_DIFF;
  420.         break;
  421.         case 'l':
  422.         canonicalize = TRUE;
  423.         break;
  424.         case 'm':            /* Only allow exact match */
  425.         ematch = TRUE;
  426.         case 'n':
  427.         diff_type = NORMAL_DIFF;
  428.         break;
  429.         case 'o':
  430.         outname = savestr(Argv[1]);
  431.         Argc--,Argv++;
  432.         break;
  433.         case 'p':
  434.         usepath = TRUE;    /* do not strip path names */
  435.         break;
  436.         case 'r':
  437.         Strcpy(rejname,Argv[1]);
  438.         Argc--,Argv++;
  439.         break;
  440.         case 'R':
  441.         reverse = TRUE;
  442.         break;
  443.         case 's':
  444.         verbose = FALSE;
  445.         break;
  446. #ifdef DEBUGGING
  447.         case 'x':
  448.         debug = atoi(s+1);
  449.         break;
  450. #endif
  451.         default:
  452.         fatal("Unrecognized switch: %s\n",Argv[0]);
  453.         }
  454.     }
  455.     }
  456. }
  457.  
  458. LINENUM
  459. locate_hunk()
  460. {
  461.     register LINENUM first_guess = pch_first() + last_offset;
  462.     register LINENUM offset;
  463.     LINENUM pat_lines = pch_ptrn_lines();
  464.     register LINENUM max_pos_offset = input_lines - first_guess
  465.                 - pat_lines + 1; 
  466.     register LINENUM max_neg_offset = first_guess - last_frozen_line - 1
  467.                 - pch_context();
  468.  
  469.     if (!pat_lines)            /* null range matches always */
  470.     return first_guess;
  471.     if (max_neg_offset >= first_guess)    /* do not try lines < 0 */
  472.     max_neg_offset = first_guess - 1;
  473.     if (first_guess <= input_lines && patch_match(first_guess,(LINENUM)0))
  474.     return first_guess;
  475.     for (offset = 1; ; offset++) {
  476.     bool check_after = (offset <= max_pos_offset);
  477.     bool check_before = (offset <= max_pos_offset);
  478.  
  479.     if (check_after && patch_match(first_guess,offset)) {
  480. #ifdef DEBUGGING
  481.         if (debug & 1)
  482.         printf("Offset changing from %d to %d\n",last_offset,offset);
  483. #endif
  484.         last_offset = offset;
  485.         return first_guess+offset;
  486.     }
  487.     else if (check_before && patch_match(first_guess,-offset)) {
  488. #ifdef DEBUGGING
  489.         if (debug & 1)
  490.         printf("Offset changing from %d to %d\n",last_offset,-offset);
  491. #endif
  492.         last_offset = -offset;
  493.         return first_guess-offset;
  494.     }
  495.     else if (!check_before && !check_after)
  496.         return Null(LINENUM);
  497.     }
  498. }
  499.  
  500. /* we did not find the pattern, dump out the hunk so they can handle it */
  501.  
  502. abort_hunk()
  503. {
  504.     register LINENUM i;
  505.     register LINENUM pat_end = pch_end();
  506.     /* add in last_offset to guess the same as the previous successful hunk */
  507.     int oldfirst = pch_first() + last_offset;
  508.     int newfirst = pch_newfirst() + last_offset;
  509.     int oldlast = oldfirst + pch_ptrn_lines() - 1;
  510.     int newlast = newfirst + pch_repl_lines() - 1;
  511.  
  512.     fprintf(rejfp,"***************\n");
  513.     for (i=0; i<=pat_end; i++) {
  514.     switch (pch_char(i)) {
  515.     case '*':
  516.         fprintf(rejfp,"*** %d,%d\n", oldfirst, oldlast);
  517.         break;
  518.     case '=':
  519.         fprintf(rejfp,"--- %d,%d -----\n", newfirst, newlast);
  520.         break;
  521.     case '\n':
  522.         fprintf(rejfp,"%s", pfetch(i));
  523.         break;
  524.     case ' ': case '-': case '+': case '!':
  525.         fprintf(rejfp,"%c %s", pch_char(i), pfetch(i));
  526.         break;
  527.     default:
  528.         say("Fatal internal error in abort_hunk().\n"); 
  529.         abort();
  530.     }
  531.     }
  532. }
  533.  
  534. /* we found where to apply it (we hope), so do it */
  535.  
  536. apply_hunk(where)
  537. LINENUM where;
  538. {
  539.     register LINENUM old = 1;
  540.     register LINENUM lastline = pch_ptrn_lines();
  541.     register LINENUM new = lastline+1;
  542.     register int def_state = 0;    /* -1 = ifndef, 1 = ifdef */
  543.  
  544.     where--;
  545.     while (pch_char(new) == '=' || pch_char(new) == '\n')
  546.     new++;
  547.     
  548.     while (old <= lastline) {
  549.     if (pch_char(old) == '-') {
  550.         copy_till(where + old - 1);
  551.         if (do_defines) {
  552.         if (def_state == 0) {
  553.             fputs(not_defined, ofp);
  554.             def_state = -1;
  555.         } else
  556.         if (def_state == 1) {
  557.             fputs(else_defined, ofp);
  558.             def_state = 2;
  559.         }
  560.         fputs(pfetch(old), ofp);
  561.         }
  562.         last_frozen_line++;
  563.         old++;
  564.     }
  565.     else if (pch_char(new) == '+') {
  566.         copy_till(where + old - 1);
  567.         if (do_defines) {
  568.         if (def_state == -1) {
  569.             fputs(else_defined, ofp);
  570.             def_state = 2;
  571.         } else
  572.         if (def_state == 0) {
  573.             fputs(if_defined, ofp);
  574.             def_state = 1;
  575.         }
  576.         }
  577.         fputs(pfetch(new),ofp);
  578.         new++;
  579.     }
  580.     else {
  581.         if (pch_char(new) != pch_char(old)) {
  582.         say("Out-of-sync patch, lines %d,%d\n",
  583.             pch_hunk_beg() + old - 1,
  584.             pch_hunk_beg() + new - 1);
  585. #ifdef DEBUGGING
  586.         printf("oldchar = '%c', newchar = '%c'\n",
  587.             pch_char(old), pch_char(new));
  588. #endif
  589.         my_exit(1);
  590.         }
  591.         if (pch_char(new) == '!') {
  592.         copy_till(where + old - 1);
  593.         if (do_defines) {
  594.            fputs(not_defined,ofp);
  595.            def_state = -1;
  596.         }
  597.         while (pch_char(old) == '!') {
  598.             if (do_defines) {
  599.             fputs(pfetch(old),ofp);
  600.             }
  601.             last_frozen_line++;
  602.             old++;
  603.         }
  604.         if (do_defines) {
  605.             fputs(else_defined, ofp);
  606.             def_state = 2;
  607.         }
  608.         while (pch_char(new) == '!') {
  609.             fputs(pfetch(new),ofp);
  610.             new++;
  611.         }
  612.         if (do_defines) {
  613.             fputs(end_defined, ofp);
  614.             def_state = 0;
  615.         }
  616.         }
  617.         else {
  618.         assert(pch_char(new) == ' ');
  619.         old++;
  620.         new++;
  621.         }
  622.     }
  623.     }
  624.     if (new <= pch_end() && pch_char(new) == '+') {
  625.     copy_till(where + old - 1);
  626.     if (do_defines) {
  627.         if (def_state == 0) {
  628.             fputs(if_defined, ofp);
  629.         def_state = 1;
  630.         } else
  631.         if (def_state == -1) {
  632.         fputs(else_defined, ofp);
  633.         def_state = 2;
  634.         }
  635.     }
  636.     while (new <= pch_end() && pch_char(new) == '+') {
  637.         fputs(pfetch(new),ofp);
  638.         new++;
  639.     }
  640.     }
  641.     if (do_defines && def_state) {
  642.     fputs(end_defined, ofp);
  643.     }
  644. }
  645.  
  646. do_ed_script()
  647. {
  648. #ifndef AMIGA
  649.     FILE *pipefp, *popen();
  650.     bool t_l_command = FALSE;
  651.     register char *t;
  652.     long beginning_of_this_line;
  653.  
  654.     Unlink(TMPOUTNAME);
  655.     copy_file(filearg[0],TMPOUTNAME);
  656.     if (verbose)
  657.     Sprintf(buf,"/bin/ed %s",TMPOUTNAME);
  658.     else
  659.     Sprintf(buf,"/bin/ed - %s",TMPOUTNAME);
  660.     pipefp = popen(buf,"w");
  661.     for (;;) {
  662.     beginning_of_this_line = ftell(pfp);
  663.     if (pgets(buf,sizeof buf,pfp) == Nullch) {
  664.         next_intuit_at(beginning_of_this_line);
  665.         break;
  666.     }
  667.     for (t=buf; isdigit(*t) || *t == ','; t++) ;
  668.     t_l_command = (isdigit(*buf) &&
  669.       (*t == 'd' || *t == 'c' || *t == 'a') );
  670.     if (t_l_command) {
  671.         fputs(buf,pipefp);
  672.         if (*t != 'd') {
  673.         while (pgets(buf,sizeof buf,pfp) != Nullch) {
  674.             fputs(buf,pipefp);
  675.             if (strEQ(buf,".\n"))
  676.             break;
  677.         }
  678.         }
  679.     }
  680.     else {
  681.         next_intuit_at(beginning_of_this_line);
  682.         break;
  683.     }
  684.     }
  685.     fprintf(pipefp,"w\n");
  686.     fprintf(pipefp,"q\n");
  687.     Fflush(pipefp);
  688.     Pclose(pipefp);
  689.     ignore_signals();
  690.     move_file(TMPOUTNAME,outname);
  691.     set_signals();
  692. #else
  693.     fatal("patch: Ed scripts not supported on Amiga\n");
  694. #endif AMIGA
  695. }
  696.  
  697. init_output(name)
  698. char *name;
  699. {
  700.     ofp = fopen(name,"w");
  701.     if (ofp == Nullfp)
  702.     fatal("patch: can't create %s.\n",name);
  703. }
  704.  
  705. init_reject(name)
  706. char *name;
  707. {
  708.     rejfp = fopen(name,"w");
  709.     if (rejfp == Nullfp)
  710.     fatal("patch: can't create %s.\n",name);
  711. }
  712.  
  713. move_file(from,to)
  714. char *from, *to;
  715. {
  716.     char bakname[512];
  717.     register char *s;
  718.     int fromfd;
  719.     register int i;
  720.  
  721.     /* to stdout? */
  722.  
  723.     if (strEQ(to,"-")) {
  724. #ifdef DEBUGGING
  725.     if (debug & 4)
  726.         say("Moving %s to stdout.\n",from);
  727. #endif
  728.     fromfd = open(from,0);
  729.     if (fromfd < 0)
  730.         fatal("patch: internal error, can't reopen %s\n",from);
  731.     while ((i=read(fromfd,buf,sizeof buf)) > 0)
  732.         if (write(1,buf,i) != i)
  733.         fatal("patch: write failed\n");
  734.     Close(fromfd);
  735.     return;
  736.     }
  737.  
  738.     Strcpy(bakname,to);
  739.     Strcat(bakname,origext?origext:ORIGEXT);
  740. #ifndef AMIGA
  741.     if (stat(to,&filestat) >= 0) {    /* output file exists */
  742.     dev_t to_device = filestat.st_dev;
  743.     ino_t to_inode  = filestat.st_ino;
  744. #else
  745.     if (access(to,0) >= 0) {    /* output file exists */
  746. #endif AMIGA
  747.     char *simplename = bakname;
  748.     
  749.     for (s=bakname; *s; s++) {
  750.         if (*s == '/')
  751.         simplename = s+1;
  752.     }
  753.     /* find a backup name that is not the same file */
  754. #ifndef AMIGA
  755.     while (stat(bakname,&filestat) >= 0 &&
  756.         to_device == filestat.st_dev && to_inode == filestat.st_ino) {
  757.         for (s=simplename; *s && !islower(*s); s++) ;
  758.         if (*s)
  759.         *s = toupper(*s);
  760.         else
  761.         Strcpy(simplename, simplename+1);
  762.     }
  763.     while (unlink(bakname) >= 0) ;    /* while() is for benefit of Eunice */
  764. #else
  765.     unlink(bakname);
  766. #endif AMIGA
  767. #ifdef DEBUGGING
  768.     if (debug & 4)
  769.         say("Moving %s to %s.\n",to,bakname);
  770. #endif
  771. #ifndef AMIGA
  772.     if (link(to,bakname) < 0) {
  773. #else
  774.     if (rename(to,bakname) < 0) {
  775. #endif AMIGA
  776.         say("patch: can't backup %s, output is in %s\n",
  777.         to,from);
  778.         return;
  779.     }
  780. #ifndef AMIGA
  781.     while (unlink(to) >= 0) ;
  782. #endif AMIGA
  783.     }
  784. #ifdef DEBUGGING
  785.     if (debug & 4)
  786.     say("Moving %s to %s.\n",from,to);
  787. #endif
  788. #ifndef AMIGA
  789.     if (link(from,to) < 0) {        /* different file system? */
  790. #else
  791.     if (rename(from,to) < 0) {        /* different file system? */
  792. #endif AMIGA
  793.     int tofd;
  794.     
  795.     tofd = creat(to,0666);
  796.     if (tofd < 0) {
  797.         say("patch: can't create %s, output is in %s.\n",
  798.           to, from);
  799.         return;
  800.     }
  801.     fromfd = open(from,0);
  802.     if (fromfd < 0)
  803.         fatal("patch: internal error, can't reopen %s\n",from);
  804.     while ((i=read(fromfd,buf,sizeof buf)) > 0)
  805.         if (write(tofd,buf,i) != i)
  806.         fatal("patch: write failed\n");
  807.     Close(fromfd);
  808.     Close(tofd);
  809. #ifdef AMIGA
  810.     Unlink(from);
  811. #endif AMIGA
  812.     }
  813. #ifndef AMIGA
  814.     Unlink(from);
  815. #endif AMIGA
  816. }
  817.  
  818. copy_file(from,to)
  819. char *from, *to;
  820. {
  821.     int tofd;
  822.     int fromfd;
  823.     register int i;
  824.     
  825.     tofd = creat(to,0666);
  826.     if (tofd < 0)
  827.     fatal("patch: can't create %s.\n", to);
  828.     fromfd = open(from,0);
  829.     if (fromfd < 0)
  830.     fatal("patch: internal error, can't reopen %s\n",from);
  831.     while ((i=read(fromfd,buf,sizeof buf)) > 0)
  832.     if (write(tofd,buf,i) != i)
  833.         fatal("patch: write (%s) failed\n", to);
  834.     Close(fromfd);
  835.     Close(tofd);
  836. }
  837.  
  838. copy_till(lastline)
  839. register LINENUM lastline;
  840. {
  841.     if (last_frozen_line > lastline)
  842.     say("patch: misordered hunks! output will be garbled.\n");
  843.     while (last_frozen_line < lastline) {
  844.     dump_line(++last_frozen_line);
  845.     }
  846. }
  847.  
  848. spew_output()
  849. {
  850.     copy_till(input_lines);        /* dump remainder of file */
  851.     Fclose(ofp);
  852.     ofp = Nullfp;
  853. }
  854.  
  855. dump_line(line)
  856. LINENUM line;
  857. {
  858.     register char *s;
  859.  
  860.     for (s=ifetch(line,0); putc(*s,ofp) != '\n'; s++) ;
  861. }
  862.  
  863. /* does the patch pattern match at line base+offset? */
  864.  
  865. bool
  866. patch_match(base,offset)
  867. LINENUM base;
  868. LINENUM offset;
  869. {
  870.     register LINENUM pline;
  871.     register LINENUM iline;
  872.     register LINENUM pat_lines = pch_ptrn_lines();
  873.  
  874.     for (pline = 1, iline=base+offset; pline <= pat_lines; pline++,iline++) {
  875.     if (canonicalize) {
  876.         if (!similar(ifetch(iline,(offset >= 0)),
  877.              pfetch(pline),
  878.              pch_line_len(pline) ))
  879.         return FALSE;
  880.     }
  881.     else if (strnNE(ifetch(iline,(offset >= 0)),
  882.            pfetch(pline),
  883.            pch_line_len(pline) ))
  884.         return FALSE;
  885.     }
  886.     return TRUE;
  887. }
  888.  
  889. /* match two lines with canonicalized white space */
  890.  
  891. bool
  892. similar(a,b,len)
  893. register char *a, *b;
  894. register int len;
  895. {
  896.     while (len) {
  897.     if (isspace(*b)) {        /* whitespace (or \n) to match? */
  898.         if (!isspace(*a))        /* no corresponding whitespace? */
  899.         return FALSE;
  900.         while (len && isspace(*b) && *b != '\n')
  901.         b++,len--;        /* skip pattern whitespace */
  902.         while (isspace(*a) && *a != '\n')
  903.         a++;            /* skip target whitespace */
  904.         if (*a == '\n' || *b == '\n')
  905. #ifndef AMIGA
  906.         return (*a == *b);    /* should end in sync */
  907. #else
  908.         return (bool)(*a == *b); /* should end in sync */
  909. #endif AMIGA
  910.     }
  911.     else if (*a++ != *b++)        /* match non-whitespace chars */
  912.         return FALSE;
  913.     else
  914.         len--;            /* probably not necessary */
  915.     }
  916.     return TRUE;            /* actually, this is not reached */
  917.                     /* since there is always a \n */
  918. }
  919.  
  920. /* input file with indexable lines abstract type */
  921.  
  922. bool using_plan_a = TRUE;
  923. static long i_size;            /* size of the input file */
  924. static char *i_womp;            /* plan a buffer for entire file */
  925. static char **i_ptr;            /* pointers to lines in i_womp */
  926.  
  927. static int tifd = -1;            /* plan b virtual string array */
  928. static char *tibuf[2];            /* plan b buffers */
  929. static LINENUM tiline[2] = {-1,-1};    /* 1st line in each buffer */
  930. static LINENUM lines_per_buf;        /* how many lines per buffer */
  931. static int tireclen;            /* length of records in tmp file */
  932.  
  933. re_input()
  934. {
  935.     if (using_plan_a) {
  936.     i_size = 0;
  937.     /*NOSTRICT*/
  938.     if (i_ptr != Null(char**))
  939.         free((char *)i_ptr);
  940.     if (i_womp != Nullch)
  941.         free(i_womp);
  942.     i_womp = Nullch;
  943.     i_ptr = Null(char **);
  944.     }
  945.     else {
  946.     using_plan_a = TRUE;        /* maybe the next one is smaller */
  947.     Close(tifd);
  948.     tifd = -1;
  949.     free(tibuf[0]);
  950.     free(tibuf[1]);
  951.     tibuf[0] = tibuf[1] = Nullch;
  952.     tiline[0] = tiline[1] = -1;
  953.     tireclen = 0;
  954.     }
  955. }
  956.  
  957. scan_input(filename)
  958. char *filename;
  959. {
  960.     bool plan_a();
  961.  
  962.     if (!plan_a(filename))
  963.     plan_b(filename);
  964. }
  965.  
  966. /* try keeping everything in memory */
  967.  
  968. bool
  969. plan_a(filename)
  970. char *filename;
  971. {
  972.     int ifd;
  973.     register char *s;
  974.     register LINENUM iline;
  975.  
  976. #ifndef AMIGA
  977.     if (stat(filename,&filestat) < 0) {
  978.     Sprintf(buf,"RCS/%s%s",filename,RCSSUFFIX);
  979.     if (stat(buf,&filestat) >= 0 || stat(buf+4,&filestat) >= 0) {
  980.         Sprintf(buf,CHECKOUT,filename);
  981.         if (verbose)
  982.         say("Can't find %s--attempting to check it out from RCS.\n",
  983.             filename);
  984.         if (system(buf) || stat(filename,&filestat))
  985.         fatal("Can't check out %s.\n",filename);
  986.     }
  987.     else {
  988.         Sprintf(buf,"SCCS/%s%s",SCCSPREFIX,filename);
  989.         if (stat(buf,&filestat) >= 0 || stat(buf+5,&filestat) >= 0) {
  990.         Sprintf(buf,GET,filename);
  991.         if (verbose)
  992.             say("Can't find %s--attempting to get it from SCCS.\n",
  993.             filename);
  994.         if (system(buf) || stat(filename,&filestat))
  995.             fatal("Can't get %s.\n",filename);
  996.         }
  997.         else
  998.         fatal("Can't find %s.\n",filename);
  999.     }
  1000.     }
  1001.     if ((filestat.st_mode & S_IFMT) & ~S_IFREG)
  1002.     fatal("%s is not a normal file--can't patch.\n",filename);
  1003.     i_size = filestat.st_size;
  1004.     /*NOSTRICT*/
  1005.     i_womp = malloc((MEM)(i_size+2));
  1006.     if (i_womp == Nullch)
  1007.     return FALSE;
  1008. #else
  1009.     if (access(filename,4) < 0)
  1010.     fatal("Can't find %s.\n",filename);
  1011. #endif AMIGA
  1012.     if ((ifd = open(filename,0)) < 0)
  1013.     fatal("Can't open file %s\n",filename);
  1014. #ifdef AMIGA
  1015.     i_size = lseek(ifd, 0L, 2);
  1016.     lseek(ifd, 0L, 0);
  1017. #endif AMIGA
  1018.     /*NOSTRICT*/
  1019. #ifdef AMIGA
  1020.     if ( (i_size <= 0) || !(i_womp = malloc((MEM)(i_size+2))) ) {
  1021.     close(ifd);
  1022.     return FALSE;
  1023.     }
  1024.     /*NOSTRICT*/
  1025. #endif AMIGA
  1026.     if (read(ifd,i_womp,(int)i_size) != i_size) {
  1027.     Close(ifd);
  1028.     free(i_womp);
  1029.     return FALSE;
  1030.     }
  1031.     Close(ifd);
  1032.     if (i_womp[i_size-1] != '\n')
  1033.     i_womp[i_size++] = '\n';
  1034.     i_womp[i_size] = '\0';
  1035.  
  1036.     /* count the lines in the buffer so we know how many pointers we need */
  1037.  
  1038.     iline = 0;
  1039.     for (s=i_womp; *s; s++) {
  1040.     if (*s == '\n')
  1041.         iline++;
  1042.     }
  1043.     /*NOSTRICT*/
  1044.     i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
  1045.     if (i_ptr == Null(char **)) {    /* shucks, it was a near thing */
  1046.     free((char *)i_womp);
  1047.     return FALSE;
  1048.     }
  1049.     
  1050.     /* now scan the buffer and build pointer array */
  1051.  
  1052.     iline = 1;
  1053.     i_ptr[iline] = i_womp;
  1054.     for (s=i_womp; *s; s++) {
  1055.     if (*s == '\n')
  1056.         i_ptr[++iline] = s+1;    /* these are NOT null terminated */
  1057.     }
  1058.     input_lines = iline - 1;
  1059.  
  1060.     /* now check for revision, if any */
  1061.  
  1062.     if (revision != Nullch) { 
  1063.     if (!rev_in_string(i_womp)) {
  1064.         ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
  1065.         revision);
  1066.         if (*buf != 'y')
  1067.         fatal("Aborted.\n");
  1068.     }
  1069.     else if (verbose)
  1070.         say("Good.  This file appears to be the %s version.\n",
  1071.         revision);
  1072.     }
  1073.     return TRUE;            /* plan a will work */
  1074. }
  1075.  
  1076. /* keep (virtually) nothing in memory */
  1077.  
  1078. plan_b(filename)
  1079. char *filename;
  1080. {
  1081.     FILE *ifp;
  1082.     register int i = 0;
  1083.     register int maxlen = 1;
  1084.     bool found_revision = (revision == Nullch);
  1085.  
  1086.     using_plan_a = FALSE;
  1087.     if ((ifp = fopen(filename,"r")) == Nullfp)
  1088.     fatal("Can't open file %s\n",filename);
  1089.     if ((tifd = creat(TMPINNAME,0666)) < 0)
  1090.     fatal("Can't open file %s\n",TMPINNAME);
  1091.     while (fgets(buf,sizeof buf, ifp) != Nullch) {
  1092.     if (revision != Nullch && !found_revision && rev_in_string(buf))
  1093.         found_revision = TRUE;
  1094.     if ((i = strlen(buf)) > maxlen)
  1095.         maxlen = i;            /* find longest line */
  1096.     }
  1097.     if (revision != Nullch) {
  1098.     if (!found_revision) {
  1099.         ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
  1100.         revision);
  1101.         if (*buf != 'y')
  1102.         fatal("Aborted.\n");
  1103.     }
  1104.     else if (verbose)
  1105.         say("Good.  This file appears to be the %s version.\n",
  1106.         revision);
  1107.     }
  1108.     Fseek(ifp,0L,0);        /* rewind file */
  1109.     lines_per_buf = BUFFERSIZE / maxlen;
  1110.     tireclen = maxlen;
  1111.     tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
  1112.     tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
  1113.     if (tibuf[1] == Nullch)
  1114.     fatal("Can't seem to get enough memory.\n");
  1115.     for (i=1; ; i++) {
  1116.     if (! (i % lines_per_buf))    /* new block */
  1117.         if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
  1118.         fatal("patch: can't write temp file.\n");
  1119.     if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
  1120.       == Nullch) {
  1121.         input_lines = i - 1;
  1122.         if (i % lines_per_buf)
  1123.         if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
  1124.             fatal("patch: can't write temp file.\n");
  1125.         break;
  1126.     }
  1127.     }
  1128.     Fclose(ifp);
  1129.     Close(tifd);
  1130.     if ((tifd = open(TMPINNAME,0)) < 0) {
  1131.     fatal("Can't reopen file %s\n",TMPINNAME);
  1132.     }
  1133. }
  1134.  
  1135. /* fetch a line from the input file, \n terminated, not necessarily \0 */
  1136. char *
  1137. ifetch(line,whichbuf)
  1138. register LINENUM line;
  1139. int whichbuf;                /* ignored when file in memory */
  1140. {
  1141.     if (line < 1 || line > input_lines)
  1142.     return "";
  1143.     if (using_plan_a)
  1144.     return i_ptr[line];
  1145.     else {
  1146.     LINENUM offline = line % lines_per_buf;
  1147.     LINENUM baseline = line - offline;
  1148.  
  1149.     if (tiline[0] == baseline)
  1150.         whichbuf = 0;
  1151.     else if (tiline[1] == baseline)
  1152.         whichbuf = 1;
  1153.     else {
  1154.         tiline[whichbuf] = baseline;
  1155.         Lseek(tifd,(long)baseline / lines_per_buf * BUFFERSIZE,0);
  1156.         if (read(tifd,tibuf[whichbuf],BUFFERSIZE) < 0)
  1157.         fatal("Error reading tmp file %s.\n",TMPINNAME);
  1158.     }
  1159.     return tibuf[whichbuf] + (tireclen*offline);
  1160.     }
  1161. }
  1162.  
  1163. /* patch abstract type */
  1164.  
  1165. static long p_filesize;            /* size of the patch file */
  1166. static LINENUM p_first;            /* 1st line number */
  1167. static LINENUM p_newfirst;        /* 1st line number of replacement */
  1168. static LINENUM p_ptrn_lines;        /* # lines in pattern */
  1169. static LINENUM p_repl_lines;        /* # lines in replacement text */
  1170. static LINENUM p_end = -1;        /* last line in hunk */
  1171. static LINENUM p_max;            /* max allowed value of p_end */
  1172. static LINENUM p_context = 3;        /* # of context lines */
  1173. static LINENUM p_input_line = 0;    /* current line # from patch file */
  1174. static char *p_line[MAXHUNKSIZE];    /* the text of the hunk */
  1175. static char p_char[MAXHUNKSIZE];    /* +, -, and ! */
  1176. static int p_len[MAXHUNKSIZE];        /* length of each line */
  1177. static int p_indent;            /* indent to patch */
  1178. static long p_base;            /* where to intuit this time */
  1179. static long p_start;            /* where intuit found a patch */
  1180.  
  1181. re_patch()
  1182. {
  1183.     p_first = (LINENUM)0;
  1184.     p_newfirst = (LINENUM)0;
  1185.     p_ptrn_lines = (LINENUM)0;
  1186.     p_repl_lines = (LINENUM)0;
  1187.     p_end = (LINENUM)-1;
  1188.     p_max = (LINENUM)0;
  1189.     p_indent = 0;
  1190. }
  1191.  
  1192. open_patch_file(filename)
  1193. char *filename;
  1194. {
  1195.     if (filename == Nullch || !*filename || strEQ(filename,"-")) {
  1196.     pfp = fopen(TMPPATNAME,"w");
  1197.     if (pfp == Nullfp)
  1198.         fatal("patch: can't create %s.\n",TMPPATNAME);
  1199.     while (fgets(buf,sizeof buf,stdin) != NULL)
  1200.         fputs(buf,pfp);
  1201.     Fclose(pfp);
  1202.     filename = TMPPATNAME;
  1203.     }
  1204.     pfp = fopen(filename,"r");
  1205.     if (pfp == Nullfp)
  1206.     fatal("patch file %s not found\n",filename);
  1207. #ifndef AMIGA
  1208.     Fstat(fileno(pfp), &filestat);
  1209.     p_filesize = filestat.st_size;
  1210. #else
  1211.     p_filesize = lseek(fileno(pfp), 0L, 2);
  1212.     lseek(fileno(pfp), 0L, 0);
  1213. #endif AMIGA
  1214.     next_intuit_at(0L);            /* start at the beginning */
  1215. }
  1216.  
  1217. bool
  1218. there_is_another_patch()
  1219. {
  1220.     bool no_input_file = (filearg[0] == Nullch);
  1221.     
  1222.     if (p_base != 0L && p_base >= p_filesize) {
  1223.     if (verbose)
  1224.         say("done\n");
  1225.     return FALSE;
  1226.     }
  1227.     if (verbose)
  1228.     say("Hmm...");
  1229.     diff_type = intuit_diff_type();
  1230.     if (!diff_type) {
  1231.     if (p_base != 0L) {
  1232.         if (verbose)
  1233.         say("  Ignoring the trailing garbage.\ndone\n");
  1234.     }
  1235.     else
  1236.         say("  I can't seem to find a patch in there anywhere.\n");
  1237.     return FALSE;
  1238.     }
  1239.     if (verbose)
  1240.     say("  %sooks like %s to me...\n",
  1241.         (p_base == 0L ? "L" : "The next patch l"),
  1242.         diff_type == CONTEXT_DIFF ? "a context diff" :
  1243.         diff_type == NORMAL_DIFF ? "a normal diff" :
  1244.         "an ed script" );
  1245.     if (p_indent && verbose)
  1246.     say("(Patch is indented %d space%s.)\n",p_indent,p_indent==1?"":"s");
  1247.     skip_to(p_start);
  1248.     if (no_input_file) {
  1249.     if (filearg[0] == Nullch) {
  1250.         ask("File to patch: ");
  1251.         filearg[0] = fetchname(buf);
  1252.     }
  1253.     else if (verbose) {
  1254.         say("Patching file %s...\n",filearg[0]);
  1255.     }
  1256.     }
  1257.     return TRUE;
  1258. }
  1259.  
  1260. intuit_diff_type()
  1261. {
  1262.     long this_line = 0;
  1263.     long previous_line;
  1264.     long first_command_line = -1;
  1265.     bool last_line_was_command = FALSE;
  1266.     bool t_l_command = FALSE;
  1267.     register int indent;
  1268.     register char *s, *t;
  1269.     char *oldname = Nullch;
  1270.     char *newname = Nullch;
  1271.     bool no_filearg = (filearg[0] == Nullch);
  1272.  
  1273.     Fseek(pfp,p_base,0);
  1274.     for (;;) {
  1275.     previous_line = this_line;
  1276.     last_line_was_command = t_l_command;
  1277.     this_line = ftell(pfp);
  1278.     indent = 0;
  1279.     if (fgets(buf,sizeof buf,pfp) == Nullch) {
  1280.         if (first_command_line >= 0L) {
  1281.                     /* nothing but deletes!? */
  1282.         p_start = first_command_line;
  1283.         return ED_DIFF;
  1284.         }
  1285.         else {
  1286.         p_start = this_line;
  1287.         return 0;
  1288.         }
  1289.     }
  1290.     for (s = buf; *s == ' ' || *s == '\t'; s++) {
  1291.         if (*s == '\t')
  1292.         indent += 8 - (indent % 8);
  1293.         else
  1294.         indent++;
  1295.     }
  1296.     for (t=s; isdigit(*t) || *t == ','; t++) ; 
  1297.     t_l_command = (isdigit(*s) &&
  1298.       (*t == 'd' || *t == 'c' || *t == 'a') );
  1299.     if (first_command_line < 0L && t_l_command) { 
  1300.         first_command_line = this_line;
  1301.         p_indent = indent;        /* assume this for now */
  1302.     }
  1303.     if (strnEQ(s,"*** ",4))
  1304.         oldname = fetchname(s+4);
  1305.     else if (strnEQ(s,"--- ",4)) {
  1306.         newname = fetchname(s+4);
  1307.         if (no_filearg) {
  1308.         if (oldname && newname) {
  1309.             if (strlen(oldname) < strlen(newname))
  1310.             filearg[0] = oldname;
  1311.             else
  1312.             filearg[0] = newname;
  1313.         }
  1314.         else if (oldname)
  1315.             filearg[0] = oldname;
  1316.         else if (newname)
  1317.             filearg[0] = newname;
  1318.         }
  1319.     }
  1320.     else if (strnEQ(s,"Index:",6)) {
  1321.         if (no_filearg) 
  1322.         filearg[0] = fetchname(s+6);
  1323.                     /* this filearg might get limboed */
  1324.     }
  1325.     else if (strnEQ(s,"Prereq:",7)) {
  1326.         for (t=s+7; isspace(*t); t++) ;
  1327.         revision = savestr(t);
  1328.         for (t=revision; *t && !isspace(*t); t++) ;
  1329.         *t = '\0';
  1330.         if (!*revision) {
  1331.         free(revision);
  1332.         revision = Nullch;
  1333.         }
  1334.     }
  1335.     if ((!diff_type || diff_type == ED_DIFF) &&
  1336.       first_command_line >= 0L &&
  1337.       strEQ(s,".\n") ) {
  1338.         p_indent = indent;
  1339.         p_start = first_command_line;
  1340.         return ED_DIFF;
  1341.     }
  1342.     if ((!diff_type || diff_type == CONTEXT_DIFF) &&
  1343.          strnEQ(s,"********",8)) {
  1344.         p_indent = indent;
  1345.         p_start = this_line;
  1346.         return CONTEXT_DIFF;
  1347.     }
  1348.     if ((!diff_type || diff_type == NORMAL_DIFF) && 
  1349.       last_line_was_command &&
  1350.       (strnEQ(s,"< ",2) || strnEQ(s,"> ",2)) ) {
  1351.         p_start = previous_line;
  1352.         p_indent = indent;
  1353.         return NORMAL_DIFF;
  1354.     }
  1355.     }
  1356. }
  1357.  
  1358. char *
  1359. fetchname(at)
  1360. char *at;
  1361. {
  1362.     char *s = savestr(at);
  1363.     char *name;
  1364.     register char *t;
  1365. #ifndef AMIGA
  1366.     char tmpbuf[200];
  1367. #endif AMIGA
  1368.  
  1369.     for (t=s; isspace(*t); t++) ;
  1370.     name = t;
  1371.     for (; *t && !isspace(*t); t++)
  1372.     if (!usepath)
  1373.         if (*t == '/')
  1374.         name = t+1;
  1375.     *t = '\0';
  1376.     name = savestr(name);
  1377. #ifndef AMIGA
  1378.     Sprintf(tmpbuf,"RCS/%s",name);
  1379. #endif AMIGA
  1380.     free(s);
  1381. #ifndef AMIGA
  1382.     if (stat(name,&filestat) < 0) {
  1383.     Strcat(tmpbuf,RCSSUFFIX);
  1384.     if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+4,&filestat) < 0) {
  1385.         Sprintf(tmpbuf,"SCCS/%s%s",SCCSPREFIX,name);
  1386.         if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+5,&filestat) < 0) {
  1387.         free(name);
  1388.         name = Nullch;
  1389.         }
  1390.     }
  1391. #else
  1392.     if (access(name,0) < 0) {
  1393.     free(name);
  1394.     name = Nullch;
  1395. #endif AMIGA
  1396.     }
  1397.     return name;
  1398. }
  1399.  
  1400. next_intuit_at(file_pos)
  1401. long file_pos;
  1402. {
  1403.     p_base = file_pos;
  1404. }
  1405.  
  1406. skip_to(file_pos)
  1407. long file_pos;
  1408. {
  1409.     char *ret;
  1410.  
  1411.     assert(p_base <= file_pos);
  1412.     if (verbose && p_base < file_pos) {
  1413.     Fseek(pfp,p_base,0);
  1414.     say("The text leading up to this was:\n--------------------------\n");
  1415.     while (ftell(pfp) < file_pos) {
  1416.         ret = fgets(buf,sizeof buf,pfp);
  1417.         assert(ret != Nullch);
  1418.         say("|%s",buf);
  1419.     }
  1420.     say("--------------------------\n");
  1421.     }
  1422.     else
  1423.     Fseek(pfp,file_pos,0);
  1424. }
  1425.  
  1426. bool
  1427. another_hunk()
  1428. {
  1429.     register char *s;
  1430.     char *ret;
  1431.     int context = 0;
  1432.  
  1433.     while (p_end >= 0) {
  1434.     free(p_line[p_end--]);
  1435.     }
  1436.     assert(p_end == -1);
  1437.  
  1438.     p_max = MAXHUNKSIZE;        /* gets reduced when --- found */
  1439.     if (diff_type == CONTEXT_DIFF) {
  1440.     long line_beginning = ftell(pfp);
  1441.     LINENUM repl_beginning = 0;
  1442.  
  1443.     ret = pgets(buf,sizeof buf, pfp);
  1444.     if (ret == Nullch || strnNE(buf,"********",8)) {
  1445.         next_intuit_at(line_beginning);
  1446.         return FALSE;
  1447.     }
  1448.     p_context = 100;
  1449.     while (p_end < p_max) {
  1450.         ret = pgets(buf,sizeof buf, pfp);
  1451.         if (ret == Nullch) {
  1452.         if (p_max - p_end < 4)
  1453.             Strcpy(buf,"  \n");    /* assume blank lines got chopped */
  1454.         else
  1455.             fatal("Unexpected end of file in patch.\n");
  1456.         }
  1457.         p_input_line++;
  1458.         if (strnEQ(buf,"********",8))
  1459.         fatal("Unexpected end of hunk at line %d.\n",
  1460.             p_input_line);
  1461.         p_char[++p_end] = *buf;
  1462.         switch (*buf) {
  1463.         case '*':
  1464.         if (p_end != 0)
  1465.             fatal("Unexpected *** at line %d: %s", p_input_line, buf);
  1466.         context = 0;
  1467.         p_line[p_end] = savestr(buf);
  1468.         for (s=buf; *s && !isdigit(*s); s++) ;
  1469.         p_first = (LINENUM) atol(s);
  1470.         while (isdigit(*s)) s++;
  1471.         for (; *s && !isdigit(*s); s++) ;
  1472.         p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
  1473.         break;
  1474.         case '-':
  1475.         if (buf[1] == '-') {
  1476.             if (p_end != p_ptrn_lines + 1 &&
  1477.             p_end != p_ptrn_lines + 2)
  1478.             fatal("Unexpected --- at line %d: %s",
  1479.                 p_input_line,buf);
  1480.             repl_beginning = p_end;
  1481.             context = 0;
  1482.             p_line[p_end] = savestr(buf);
  1483.             p_char[p_end] = '=';
  1484.             for (s=buf; *s && !isdigit(*s); s++) ;
  1485.             p_newfirst = (LINENUM) atol(s);
  1486.             while (isdigit(*s)) s++;
  1487.             for (; *s && !isdigit(*s); s++) ;
  1488.             p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end;
  1489.             break;
  1490.         }
  1491.         /* FALL THROUGH */
  1492.         case '+': case '!':
  1493.         if (context > 0) {
  1494.             if (context < p_context)
  1495.             p_context = context;
  1496.             context = -100;
  1497.         }
  1498.         p_line[p_end] = savestr(buf+2);
  1499.         break;
  1500.         case '\t': case '\n':    /* assume the 2 spaces got eaten */
  1501.         p_line[p_end] = savestr(buf);
  1502.         if (p_end != p_ptrn_lines + 1) {
  1503.             context++;
  1504.             p_char[p_end] = ' ';
  1505.         }
  1506.         break;
  1507.         case ' ':
  1508.         context++;
  1509.         p_line[p_end] = savestr(buf+2);
  1510.         break;
  1511.         default:
  1512.         fatal("Malformed patch at line %d: %s",p_input_line,buf);
  1513.         }
  1514.         p_len[p_end] = strlen(p_line[p_end]);
  1515.                     /* for strncmp() so we do not have */
  1516.                     /* to assume null termination */
  1517.     }
  1518.     if (p_end >=0 && !p_ptrn_lines)
  1519.         fatal("No --- found in patch at line %d\n", pch_hunk_beg());
  1520.     p_repl_lines = p_end - repl_beginning;
  1521.     }
  1522.     else {                /* normal diff--fake it up */
  1523.     char hunk_type;
  1524.     register int i;
  1525.     LINENUM min, max;
  1526.     long line_beginning = ftell(pfp);
  1527.  
  1528.     p_context = 0;
  1529.     ret = pgets(buf,sizeof buf, pfp);
  1530.     p_input_line++;
  1531.     if (ret == Nullch || !isdigit(*buf)) {
  1532.         next_intuit_at(line_beginning);
  1533.         return FALSE;
  1534.     }
  1535.     p_first = (LINENUM)atol(buf);
  1536.     for (s=buf; isdigit(*s); s++) ;
  1537.     if (*s == ',') {
  1538.         p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
  1539.         while (isdigit(*s)) s++;
  1540.     }
  1541.     else
  1542.         p_ptrn_lines = (*s != 'a');
  1543.     hunk_type = *s;
  1544.     if (hunk_type == 'a')
  1545.         p_first++;            /* do append rather than insert */
  1546.     min = (LINENUM)atol(++s);
  1547.     for (; isdigit(*s); s++) ;
  1548.     if (*s == ',')
  1549.         max = (LINENUM)atol(++s);
  1550.     else
  1551.         max = min;
  1552.     if (hunk_type == 'd')
  1553.         min++;
  1554.     p_end = p_ptrn_lines + 1 + max - min + 1;
  1555.     p_newfirst = min;
  1556.     p_repl_lines = max - min + 1;
  1557.     Sprintf(buf,"*** %d,%d\n", p_first, p_first + p_ptrn_lines - 1);
  1558.     p_line[0] = savestr(buf);
  1559.     p_char[0] = '*';
  1560.     for (i=1; i<=p_ptrn_lines; i++) {
  1561.         ret = pgets(buf,sizeof buf, pfp);
  1562.         p_input_line++;
  1563.         if (ret == Nullch)
  1564.         fatal("Unexpected end of file in patch at line %d.\n",
  1565.           p_input_line);
  1566.         if (*buf != '<')
  1567.         fatal("< expected at line %d of patch.\n", p_input_line);
  1568.         p_line[i] = savestr(buf+2);
  1569.         p_len[i] = strlen(p_line[i]);
  1570.         p_char[i] = '-';
  1571.     }
  1572.     if (hunk_type == 'c') {
  1573.         ret = pgets(buf,sizeof buf, pfp);
  1574.         p_input_line++;
  1575.         if (ret == Nullch)
  1576.         fatal("Unexpected end of file in patch at line %d.\n",
  1577.             p_input_line);
  1578.         if (*buf != '-')
  1579.         fatal("--- expected at line %d of patch.\n", p_input_line);
  1580.     }
  1581.     Sprintf(buf,"--- %d,%d\n",min,max);
  1582.     p_line[i] = savestr(buf);
  1583.     p_char[i] = '=';
  1584.     for (i++; i<=p_end; i++) {
  1585.         ret = pgets(buf,sizeof buf, pfp);
  1586.         p_input_line++;
  1587.         if (ret == Nullch)
  1588.         fatal("Unexpected end of file in patch at line %d.\n",
  1589.             p_input_line);
  1590.         if (*buf != '>')
  1591.         fatal("> expected at line %d of patch.\n", p_input_line);
  1592.         p_line[i] = savestr(buf+2);
  1593.         p_len[i] = strlen(p_line[i]);
  1594.         p_char[i] = '+';
  1595.     }
  1596.     }
  1597.     if (reverse)            /* backwards patch? */
  1598.     pch_swap();
  1599. #ifdef DEBUGGING
  1600.     if (debug & 2) {
  1601.     int i;
  1602.     char special;
  1603.  
  1604.     for (i=0; i <= p_end; i++) {
  1605.         if (i == p_ptrn_lines)
  1606.         special = '^';
  1607.         else
  1608.         special = ' ';
  1609.         printf("%3d %c %c %s",i,p_char[i],special,p_line[i]);
  1610.     }
  1611.     }
  1612. #endif
  1613.     return TRUE;
  1614. }
  1615.  
  1616. char *
  1617. pgets(bf,sz,fp)
  1618. char *bf;
  1619. int sz;
  1620. FILE *fp;
  1621. {
  1622.     char *ret = fgets(bf,sz,fp);
  1623.     register char *s;
  1624.     register int indent = 0;
  1625.  
  1626.     if (p_indent && ret != Nullch) {
  1627.     for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) {
  1628.         if (*s == '\t')
  1629.         indent += 8 - (indent % 7);
  1630.         else
  1631.         indent++;
  1632.     }
  1633.     if (buf != s)
  1634.         Strcpy(buf,s);
  1635.     }
  1636.     return ret;
  1637. }
  1638.  
  1639. pch_swap()
  1640. {
  1641.     char *tp_line[MAXHUNKSIZE];        /* the text of the hunk */
  1642.     char tp_char[MAXHUNKSIZE];        /* +, -, and ! */
  1643.     int tp_len[MAXHUNKSIZE];        /* length of each line */
  1644.     register LINENUM i, n;
  1645.     bool blankline = FALSE;
  1646.     register char *s;
  1647.  
  1648.     i = p_first;
  1649.     p_first = p_newfirst;
  1650.     p_newfirst = i;
  1651.     
  1652.     /* make a scratch copy */
  1653.  
  1654.     for (i=0; i<=p_end; i++) {
  1655.     tp_line[i] = p_line[i];
  1656.     tp_char[i] = p_char[i];
  1657.     tp_len[i] = p_len[i];
  1658.     }
  1659.  
  1660.     /* now turn the new into the old */
  1661.  
  1662.     i = p_ptrn_lines + 1;
  1663.     if (tp_char[i] == '\n') {        /* account for possible blank line */
  1664.     blankline = TRUE;
  1665.     i++;
  1666.     }
  1667.     for (n=0; i <= p_end; i++,n++) {
  1668.     p_line[n] = tp_line[i];
  1669.     p_char[n] = tp_char[i];
  1670.     if (p_char[n] == '+')
  1671.         p_char[n] = '-';
  1672.     p_len[n] = tp_len[i];
  1673.     }
  1674.     if (blankline) {
  1675.     i = p_ptrn_lines + 1;
  1676.     p_line[n] = tp_line[i];
  1677.     p_char[n] = tp_char[i];
  1678.     p_len[n] = tp_len[i];
  1679.     n++;
  1680.     }
  1681.     assert(p_char[0] == '=');
  1682.     p_char[0] = '*';
  1683.     for (s=p_line[0]; *s; s++)
  1684.     if (*s == '-')
  1685.         *s = '*';
  1686.  
  1687.     /* now turn the old into the new */
  1688.  
  1689.     assert(tp_char[0] == '*');
  1690.     tp_char[0] = '=';
  1691.     for (s=tp_line[0]; *s; s++)
  1692.     if (*s == '*')
  1693.         *s = '-';
  1694.     for (i=0; n <= p_end; i++,n++) {
  1695.     p_line[n] = tp_line[i];
  1696.     p_char[n] = tp_char[i];
  1697.     if (p_char[n] == '-')
  1698.         p_char[n] = '+';
  1699.     p_len[n] = tp_len[i];
  1700.     }
  1701.     assert(i == p_ptrn_lines + 1);
  1702.     i = p_ptrn_lines;
  1703.     p_ptrn_lines = p_repl_lines;
  1704.     p_repl_lines = i;
  1705. }
  1706.  
  1707. LINENUM
  1708. pch_first()
  1709. {
  1710.     return p_first;
  1711. }
  1712.  
  1713. LINENUM
  1714. pch_ptrn_lines()
  1715. {
  1716.     return p_ptrn_lines;
  1717. }
  1718.  
  1719. LINENUM
  1720. pch_newfirst()
  1721. {
  1722.     return p_newfirst;
  1723. }
  1724.  
  1725. LINENUM
  1726. pch_repl_lines()
  1727. {
  1728.     return p_repl_lines;
  1729. }
  1730.  
  1731. LINENUM
  1732. pch_end()
  1733. {
  1734.     return p_end;
  1735. }
  1736.  
  1737. LINENUM
  1738. pch_context()
  1739. {
  1740.     return p_context;
  1741. }
  1742.  
  1743. pch_line_len(line)
  1744. LINENUM line;
  1745. {
  1746.     return p_len[line];
  1747. }
  1748.  
  1749. char
  1750. pch_char(line)
  1751. LINENUM line;
  1752. {
  1753.     return p_char[line];
  1754. }
  1755.  
  1756. char *
  1757. pfetch(line)
  1758. LINENUM line;
  1759. {
  1760.     return p_line[line];
  1761. }
  1762.  
  1763. LINENUM
  1764. pch_hunk_beg()
  1765. {
  1766.     return p_input_line - p_end - 1;
  1767. }
  1768.  
  1769. char *
  1770. savestr(s)
  1771. register char *s;
  1772. {
  1773.     register char  *rv,
  1774.                    *t;
  1775.  
  1776.     t = s;
  1777.     while (*t++);
  1778.     rv = malloc((MEM) (t - s));
  1779.     if (rv == NULL)
  1780.     fatal ("patch: out of memory (savestr)\n");
  1781.     t = rv;
  1782.     while (*t++ = *s++);
  1783.     return rv;
  1784. }
  1785.  
  1786. my_exit(status)
  1787. int status;
  1788. {
  1789.     Unlink(TMPINNAME);
  1790.     Unlink(TMPOUTNAME);
  1791.     Unlink(TMPREJNAME);
  1792.     Unlink(TMPPATNAME);
  1793.     exit(status);
  1794. }
  1795.  
  1796. #ifdef lint
  1797.  
  1798. /*VARARGS ARGSUSED*/
  1799. say(pat) char *pat; { ; }
  1800. /*VARARGS ARGSUSED*/
  1801. fatal(pat) char *pat; { ; }
  1802. /*VARARGS ARGSUSED*/
  1803. ask(pat) char *pat; { ; }
  1804.  
  1805. #else lint
  1806.  
  1807. say(pat,arg1,arg2,arg3)
  1808. char *pat;
  1809. char *arg1,*arg2,*arg3;
  1810. {
  1811.     fprintf(stderr,pat,arg1,arg2,arg3);
  1812.     Fflush(stderr);
  1813. }
  1814.  
  1815. fatal(pat,arg1,arg2,arg3)
  1816. char *pat;
  1817. char *arg1,*arg2,*arg3;
  1818. {
  1819.     say(pat,arg1,arg2,arg3);
  1820.     my_exit(1);
  1821. }
  1822.  
  1823. ask(pat,arg1,arg2,arg3)
  1824. char *pat;
  1825. char *arg1,*arg2,*arg3;
  1826. {
  1827.     int ttyfd = open("/dev/tty",2);
  1828.     int r;
  1829.  
  1830.     say(pat,arg1,arg2,arg3);
  1831.     if (ttyfd >= 0) {
  1832.     r = read(ttyfd, buf, sizeof buf);
  1833.     Close(ttyfd);
  1834.     }
  1835.     else
  1836.     r = read(2, buf, sizeof buf);
  1837.     if (r <= 0)
  1838.     buf[0] = 0;
  1839. }
  1840. #endif lint
  1841.  
  1842. bool
  1843. rev_in_string(string)
  1844. char *string;
  1845. {
  1846.     register char *s;
  1847.     register int patlen;
  1848.  
  1849.     if (revision == Nullch)
  1850.     return TRUE;
  1851.     patlen = strlen(revision);
  1852.     for (s = string; *s; s++) {
  1853.     if (isspace(*s) && strnEQ(s+1,revision,patlen) && 
  1854.         isspace(s[patlen+1] )) {
  1855.         return TRUE;
  1856.     }
  1857.     }
  1858.     return FALSE;
  1859. }
  1860.  
  1861. set_signals()
  1862. {
  1863.     /*NOSTRICT*/
  1864. #ifndef AMIGA
  1865.     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
  1866.     Signal(SIGHUP, my_exit);
  1867.     /*NOSTRICT*/
  1868. #endif AMIGA
  1869.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  1870.     Signal(SIGINT, my_exit);
  1871. }
  1872.  
  1873. ignore_signals()
  1874. {
  1875.     /*NOSTRICT*/
  1876. #ifndef AMIGA
  1877.     Signal(SIGHUP, SIG_IGN);
  1878.     /*NOSTRICT*/
  1879. #endif AMIGA
  1880.     Signal(SIGINT, SIG_IGN);
  1881. #ifdef AMIGA
  1882. }
  1883.  
  1884. char *mktemp(name)
  1885. char *name;
  1886. {
  1887.     register char *cp;
  1888.     register long tval;
  1889.     long time();
  1890.  
  1891.     cp = name + strlen(name) - 6;
  1892.     if (*cp == 'X') {
  1893.     *cp = 'A';
  1894.         tval = (unsigned long)time((long *)0) % 100000L;
  1895.     sprintf(cp+1, "%.5lu", tval);
  1896.     while ( !access(name, 0) ) {
  1897.         ++(*cp);
  1898.     }
  1899.     }
  1900.     return name;
  1901. #endif AMIGA
  1902. }
  1903.